package cn.t.facade.user;


import cn.t.converter.user.ResourceInfoDTOConverter;
import cn.t.converter.user.ResourceTreeConverter;
import cn.t.core.exception.BizException;
import cn.t.core.facade.FacadeImpl;
import cn.t.dto.user.ResourceInfoDTO;
import cn.t.core.enums.base.CommonError;
import cn.t.model.user.ParticipantRoleDO;
import cn.t.model.user.ResourceInfoDO;
import cn.t.model.user.RoleResourceDO;
import cn.t.service.user.ParticipantRoleService;
import cn.t.service.user.ResourceInfoService;
import cn.t.service.user.RoleResourceService;
import cn.t.user.event.UserRedisClearEvent;
import cn.t.vo.user.ResourceTreeVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.t.constants.Constants.REDIS_KEY__RESOURCE_PARTICIPANT_VO;
import static cn.t.constants.Constants.REDIS_KEY__RESOURCE_ROLE_ALL_VO;
import static cn.t.constants.Constants.REDIS_KEY__RESOURCE_ROLE_VO;


/**
 * 资源权限表服务编排
 *
 * @author l
 */
@Slf4j
@Service
@AllArgsConstructor
public class ResourceInfoFacade extends FacadeImpl<ResourceInfoDTO, ResourceInfoDO, ResourceInfoDTOConverter, ResourceInfoService> {

    public List<ResourceInfoDTO> findByRoleId(Long roleId) {

        List<ResourceInfoDO> list = service.findByRoleId(roleId);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.stream().map(converter::model2Dto).collect(Collectors.toList());
    }

    public List<ResourceInfoDTO> isExistsByIdAndFullFill(List<ResourceInfoDTO> resourceInfoDtoList) {
        if (resourceInfoDtoList == null || resourceInfoDtoList.isEmpty()) {
            return null;
        }
        return resourceInfoDtoList.stream().map(resourceInfoDTO -> {
            ResourceInfoDO resourceInfoDO = service.findById(resourceInfoDTO.getId());
            if (resourceInfoDO == null || resourceInfoDO.getId() == null || !resourceInfoDO.getId().equals(resourceInfoDTO.getId())) {
                String errorMessage = String.format("指定的资源不存在 resourceId=%s ", resourceInfoDTO.getId());
                log.error(errorMessage);
                throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
            }


            return converter.model2Dto(resourceInfoDO);
        }).collect(Collectors.toList());
    }


    public List<ResourceTreeVO> getResourceTree() {
        List<ResourceTreeVO> result = (List<ResourceTreeVO>) redissonClient.getBucket(REDIS_KEY__RESOURCE_ROLE_ALL_VO).get();
        if (result != null && !result.isEmpty()) {
            return result;
        }

        result = resourceTreeConverter.convert(service.getResourceMap());

        redissonClient.getBucket(REDIS_KEY__RESOURCE_ROLE_ALL_VO).set(result, 1, TimeUnit.HOURS);
        return result;
    }


    public void clearMenuTree(Long userId) {
        String redisKey = String.format(REDIS_KEY__RESOURCE_PARTICIPANT_VO, userId);
        redissonClient.getBucket(redisKey).delete();
    }

    public List<ResourceTreeVO> getMenuTreeByUser(Long userId, List<Long> participantIds) {
        String redisKey = String.format(REDIS_KEY__RESOURCE_PARTICIPANT_VO, userId);
        List<ResourceTreeVO> result = (List<ResourceTreeVO>) redissonClient.getBucket(redisKey).get();
        if (result != null && !result.isEmpty()) {
            return result;
        }

        if (ObjectUtils.isEmpty(userId) || CollectionUtils.isEmpty(participantIds)) {
            return Collections.emptyList();
        }

        List<ParticipantRoleDO> participantRoleList = new ArrayList<>();
        for (Long participantId : participantIds) {
            List<ParticipantRoleDO> tmp = participantRoleService.findMenuByParticipantId(participantId);
            if (!CollectionUtils.isEmpty(tmp)) {
                participantRoleList.addAll(tmp);
            }
        }

        Set<Long> roleIdList = participantRoleList.stream().map(ParticipantRoleDO::getRoleId).collect(Collectors.toSet());
        List<RoleResourceDO> tmp = roleResourceService.findByRoleId(roleIdList);
        if (tmp == null || tmp.isEmpty()) {
            return null;
        }
        Set<Long> resourceId = tmp.stream().map(RoleResourceDO::getResourceId).collect(Collectors.toSet());

        result = resourceTreeConverter.convert(resourceId, service.getResourceMap());

        redissonClient.getBucket(redisKey).set(result, 1, TimeUnit.HOURS);
        return result;
    }

    public List<ResourceTreeVO> findResourceTreeByRole(Long roleId) {

        String redisKey = String.format(REDIS_KEY__RESOURCE_ROLE_VO, roleId);
        List<ResourceTreeVO> result = (List<ResourceTreeVO>) redissonClient.getBucket(redisKey).get();
        if (result != null && !result.isEmpty()) {
            return result;
        }

        List<RoleResourceDO> tmp = roleResourceService.findByRoleId(roleId);
        if (tmp == null || tmp.isEmpty()) {
            return null;
        }
        Set<Long> resourceId = tmp.stream().map(RoleResourceDO::getResourceId).collect(Collectors.toSet());

        result = resourceTreeConverter.convert(resourceId, service.getResourceMap());

        redissonClient.getBucket(redisKey).set(result, 1, TimeUnit.HOURS);
        return result;
    }

    @Override
    public boolean add(ResourceInfoDTO dto) {

        if (dto != null && StringUtils.isNotBlank(dto.getResourceCode())) {
            List<ResourceInfoDO> exists = service.findByCode(dto.getResourceCode());
            if (!CollectionUtils.isEmpty(exists)) {
                String errorMessage = String.format("资源编码已存在。resourceCode=%s ", dto.getResourceCode());
                log.error(errorMessage);
                throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
            }
        }

        if (dto != null && StringUtils.isNotBlank(dto.getResourceName())) {
            List<ResourceInfoDO> exists = service.findByName(dto.getResourceName());
            if (!CollectionUtils.isEmpty(exists)) {
                String errorMessage = String.format("资源编码已存在。resourceName=%s ", dto.getResourceName());
                log.error(errorMessage);
                throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
            }
        }


        applicationEventPublisher.publishEvent(new UserRedisClearEvent(REDIS_KEY__RESOURCE_ROLE_ALL_VO, REDIS_KEY__RESOURCE_ROLE_ALL_VO.replace("all", "*")));
        return service.add(converter.dto2Model(dto));
    }

    @Override
    public boolean update(ResourceInfoDTO dto) {

        if (dto != null && StringUtils.isNotBlank(dto.getResourceCode())) {
            List<ResourceInfoDO> exists = service.findByCode(dto.getResourceCode());
            if (!CollectionUtils.isEmpty(exists)) {
                for (ResourceInfoDO resourceInfoDO : exists) {
                    if (!resourceInfoDO.getId().equals(dto.getId())) {
                        String errorMessage = String.format("资源编码已存在。resourceCode=%s ", dto.getResourceCode());
                        log.error(errorMessage);
                        throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
                    }
                }
            }
        }
        if (dto != null && StringUtils.isNotBlank(dto.getResourceName())) {
            List<ResourceInfoDO> exists = service.findByName(dto.getResourceName());
            if (!CollectionUtils.isEmpty(exists)) {
                for (ResourceInfoDO resourceInfoDO : exists) {
                    if (!resourceInfoDO.getId().equals(dto.getId())) {
                        String errorMessage = String.format("资源编码已存在。resourceName=%s ", dto.getResourceName());
                        log.error(errorMessage);
                        throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
                    }
                }
            }
        }

        applicationEventPublisher.publishEvent(new UserRedisClearEvent(REDIS_KEY__RESOURCE_ROLE_ALL_VO, REDIS_KEY__RESOURCE_ROLE_ALL_VO.replace("all", "*")));
        return service.update(converter.dto2Model(dto));
    }

    @Override
    public boolean remove(Long id) {
        List<ResourceInfoDO> children = service.findByParentId(id);
        if (children != null && !children.isEmpty()) {
            String errorMessage = String.format("存在子菜单或按钮，不可删除。resourceId=%d ", id);
            log.error(errorMessage);
            throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
        }

        boolean result = service.removeById(id);
        roleResourceService.removeByResourceId(id);
        applicationEventPublisher.publishEvent(new UserRedisClearEvent(REDIS_KEY__RESOURCE_ROLE_ALL_VO, REDIS_KEY__RESOURCE_ROLE_ALL_VO.replace("all", "*")));


        return result;
    }

    public List<String> getResourceCode() {
        List<ResourceTreeVO> source = getResourceTree();
        return getResourceCode(source);
    }

    private List<String> getResourceCode(List<ResourceTreeVO> source) {

        if (CollectionUtils.isEmpty(source)) {
            return Collections.emptyList();
        }

        List<String> ret = new ArrayList<>();
        for (ResourceTreeVO tree : source) {
            if (StringUtils.isNotBlank(tree.getResourceCode())) {
                ret.add(tree.getResourceCode());
            }
            if (!CollectionUtils.isEmpty(tree.getChildren())) {
                ret.addAll(getResourceCode(tree.getChildren()));
            }
        }

        return ret;
    }


    @Autowired
    private ResourceTreeConverter resourceTreeConverter;

    @Autowired
    private ParticipantRoleService participantRoleService;

    @Autowired
    private RoleResourceService roleResourceService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;


}

